package org.convallaria.infrastruct.ce.monitor;

import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Timer;
import lombok.extern.slf4j.Slf4j;
import org.convallaria.infrastruct.ce.properties.CacheProperties;
import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;

import java.util.concurrent.atomic.AtomicLong;

/**
 * 缓存指标收集器
 * 收集和暴露缓存相关的监控指标
 * 
 * @author gjh
 * @since 2024-09-24
 */
@Slf4j
@Configuration
@EnableScheduling
@ConditionalOnClass({CacheManager.class, MeterRegistry.class})
@ConditionalOnBean({CacheManager.class, MeterRegistry.class})
public class CacheMetricsCollector {

    private final CacheManager cacheManager;
    private final CacheProperties cacheProperties;
    private final MeterRegistry meterRegistry;
    
    // 缓存操作计数器
    private final AtomicLong cacheHits = new AtomicLong(0);
    private final AtomicLong cacheMisses = new AtomicLong(0);
    private final AtomicLong cacheEvictions = new AtomicLong(0);
    private final AtomicLong cachePuts = new AtomicLong(0);

    // 缓存操作计时器
    private final Timer cacheGetTimer;
    private final Timer cachePutTimer;

    @Autowired
    public CacheMetricsCollector(CacheManager cacheManager, CacheProperties cacheProperties, MeterRegistry meterRegistry) {
        this.cacheManager = cacheManager;
        this.cacheProperties = cacheProperties;
        this.meterRegistry = meterRegistry;
        
        // 初始化计时器
        this.cacheGetTimer = Timer.builder("cache.get.duration")
                .description("缓存获取操作耗时")
                .register(meterRegistry);
        
        this.cachePutTimer = Timer.builder("cache.put.duration")
                .description("缓存存储操作耗时")
                .register(meterRegistry);
    }

    /**
     * 自定义MeterRegistry配置
     */
    @Bean
    public MeterRegistryCustomizer<MeterRegistry> cacheMetricsCustomizer() {
        return registry -> {
            // 注册缓存操作计数器 - 使用Gauge而不是Counter来避免重复计数
            registry.gauge("cache.operations", 
                io.micrometer.core.instrument.Tags.of("operation", "hit"), 
                cacheHits, AtomicLong::get);
            
            registry.gauge("cache.operations", 
                io.micrometer.core.instrument.Tags.of("operation", "miss"), 
                cacheMisses, AtomicLong::get);
            
            registry.gauge("cache.operations", 
                io.micrometer.core.instrument.Tags.of("operation", "evict"), 
                cacheEvictions, AtomicLong::get);
            
            registry.gauge("cache.operations", 
                io.micrometer.core.instrument.Tags.of("operation", "put"), 
                cachePuts, AtomicLong::get);

            // 注册缓存命中率指标
            registry.gauge("cache.hit.ratio", this, CacheMetricsCollector::getHitRatio);

            // 注册缓存大小指标
            registry.gauge("cache.size", this, CacheMetricsCollector::getTotalCacheSize);
        };
    }

    /**
     * 定期收集缓存指标
     */
    @Scheduled(fixedDelayString = "${convallaria.cache.monitoring.metrics-interval:PT1M}")
    public void collectMetrics() {
        if (!cacheProperties.getMonitoring().isEnabled()) {
            return;
        }

        try {
            // 收集各缓存的详细指标
            for (String cacheName : cacheManager.getCacheNames()) {
                collectCacheMetrics(cacheName);
            }
            
            log.debug("缓存指标收集完成 - 命中: {}, 未命中: {}, 驱逐: {}, 存储: {}", 
                    cacheHits.get(), cacheMisses.get(), cacheEvictions.get(), cachePuts.get());
            
        } catch (Exception e) {
            log.warn("收集缓存指标时发生错误", e);
        }
    }

    /**
     * 收集指定缓存的指标
     */
    private void collectCacheMetrics(String cacheName) {
        try {
            Cache cache = cacheManager.getCache(cacheName);
            if (cache == null) {
                return;
            }

            Object nativeCache = cache.getNativeCache();
            
            // 对于Caffeine缓存，收集详细统计信息
            if (nativeCache instanceof com.github.benmanes.caffeine.cache.Cache) {
                com.github.benmanes.caffeine.cache.Cache<?, ?> caffeineCache = 
                    (com.github.benmanes.caffeine.cache.Cache<?, ?>) nativeCache;
                
                // 获取Caffeine统计信息
                com.github.benmanes.caffeine.cache.stats.CacheStats stats = caffeineCache.stats();
                
                // 记录到MeterRegistry - 使用正确的gauge方法
                meterRegistry.gauge("cache.caffeine.hit.count", 
                    io.micrometer.core.instrument.Tags.of("cache", cacheName), stats.hitCount());
                meterRegistry.gauge("cache.caffeine.miss.count", 
                    io.micrometer.core.instrument.Tags.of("cache", cacheName), stats.missCount());
                meterRegistry.gauge("cache.caffeine.hit.ratio", 
                    io.micrometer.core.instrument.Tags.of("cache", cacheName), stats.hitRate());
                meterRegistry.gauge("cache.caffeine.eviction.count", 
                    io.micrometer.core.instrument.Tags.of("cache", cacheName), stats.evictionCount());
                meterRegistry.gauge("cache.caffeine.size", 
                    io.micrometer.core.instrument.Tags.of("cache", cacheName), caffeineCache.estimatedSize());
                
                log.debug("收集Caffeine缓存指标: {} - 命中率: {:.2f}%, 大小: {}", 
                    cacheName, stats.hitRate() * 100, caffeineCache.estimatedSize());
            }
            
            // 对于Redis缓存，这里可以添加Redis特定的指标收集
            if (nativeCache.getClass().getName().contains("redis")) {
                // 这里可以调用Redis的INFO命令获取详细信息
                // 例如：内存使用、键数量、命中率等
                log.debug("收集Redis缓存指标: {}", cacheName);
            }
            
        } catch (Exception e) {
            log.warn("收集缓存指标失败: {}", cacheName, e);
        }
    }

    /**
     * 记录缓存命中
     */
    public void recordHit() {
        cacheHits.incrementAndGet();
    }

    /**
     * 记录缓存未命中
     */
    public void recordMiss() {
        cacheMisses.incrementAndGet();
    }

    /**
     * 记录缓存驱逐
     */
    public void recordEviction() {
        cacheEvictions.incrementAndGet();
    }

    /**
     * 记录缓存存储
     */
    public void recordPut() {
        cachePuts.incrementAndGet();
    }

    /**
     * 记录缓存获取操作耗时
     */
    public void recordGetDuration(Runnable operation) {
        cacheGetTimer.record(operation);
    }

    /**
     * 记录缓存存储操作耗时
     */
    public void recordPutDuration(Runnable operation) {
        cachePutTimer.record(operation);
    }

    /**
     * 获取缓存命中率
     */
    public double getHitRatio() {
        long hits = cacheHits.get();
        long misses = cacheMisses.get();
        long total = hits + misses;
        return total > 0 ? (double) hits / total : 0.0;
    }

    /**
     * 获取总缓存大小
     */
    public long getTotalCacheSize() {
        // 这里可以实现获取总缓存大小的逻辑
        // 对于Redis，可能需要特殊处理
        return cacheManager.getCacheNames().size();
    }

    /**
     * 重置所有指标
     */
    public void resetMetrics() {
        cacheHits.set(0);
        cacheMisses.set(0);
        cacheEvictions.set(0);
        cachePuts.set(0);
        log.info("缓存指标已重置");
    }
}
